using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing.Design;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using ICSharpCode.SharpZipLib.Zip;
using TmxLib;
using TmxUtil;

namespace TmxEditor
{
    [Serializable]
    public class SnapshotInfo: ISerializable
    {
        private string _fullName;
        private string _workingFolder;
        
        [NonSerialized]
        private XmlDocument _manifest = null;

        public SnapshotInfo(string fullName, XmlDocument manifest)
        {
            _fullName = fullName;
            _manifest = manifest;
        }


        public SnapshotInfo(SerializationInfo info, StreamingContext context)
        {
            string m = info.GetString("mainfest_string");
            if (m != null)
            {
                _manifest = new XmlDocument();
                StringReader rdr = new StringReader(m);
                _manifest.Load(rdr);
            }

            _fullName = info.GetString("full_name");
            _workingFolder = info.GetString("working_folder");
        }


        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (_manifest != null)
            {
                StringBuilder m = new StringBuilder();
                _manifest.Save(new StringWriter(m));
                info.AddValue("mainfest_string", m.ToString());
            }

            info.AddValue("full_name", _fullName);
            info.AddValue("working_folder", _workingFolder);
        }

        

        public string FullName
        {
            get { return _fullName; }
        }

        public bool HasManifest
        {
            get { return _manifest != null; }
        }

        public string WorkingFolder
        {
            get
            {
                if (_workingFolder != null)
                    return _workingFolder;

                string random = "Preview\\" + Guid.NewGuid().ToString("N");
                string temp = Environment.GetEnvironmentVariable("TEMP");
                if (!string.IsNullOrEmpty(temp))
                {
                    temp = new DirectoryInfo(temp).FullName;
                    string r = Path.Combine(temp, random);
                    try {
                        Directory.CreateDirectory(r);
                    }
                    catch {}

                    if (Directory.Exists(r))
                    {
                        _workingFolder = r;
                        return _workingFolder;
                    }
                }

                temp = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                {
                    temp = new DirectoryInfo(temp).FullName;
                    string r = Path.Combine(temp, random);
                    try
                    {
                        Directory.CreateDirectory(r);
                    }
                    catch { }

                    if (Directory.Exists(r))
                    {
                        _workingFolder = r;
                        return _workingFolder;
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not create working folder: " + r);
                    }
                }
            }
        }

        public string SolutionFile
        {
            get
            {
                return _manifest.DocumentElement.GetAttribute("solution");
            }
        }

        public SnapshotBinaryItemInfoCollection AssembliesInfo
        {
            get
            {
                return SnapshotBinaryItemInfoCollection.Parse(_manifest.DocumentElement);
            }
        }

        public string SolutionFullName
        {
            get { return Path.Combine(WorkingFolder, SolutionFile); }
        }

        public void RunPreview(string atAssemblyName, string atTypeName)
        {
            SnaphotLoader.Load(this, atAssemblyName, atTypeName);
            Debug.WriteLine("Preview '" + atTypeName + "' from '" + atAssemblyName + "'");

        }


        public void Build()
        {
            SnapshotBinaryItemInfoCollection asmInfoList = AssembliesInfo;
            Solution solution = SolutionParser.Parse(SolutionFullName);
            foreach(Project p in solution.Projects)
            {
                ResXCollection resXlist = new ResXCollection(p.ResXList);
                string[] cultures = resXlist.GetCultures();
                foreach(string culture in cultures)
                {
                    if (culture != string.Empty)
                    {
                        ResX[] localResourceList = resXlist.FindByCulture(culture);
                        string[] localFiles = new string[localResourceList.Length];
                        for (int i = 0; i < localFiles.Length; i++)
                            localFiles[i] = localResourceList[i].File;

                        string binFile = null;
                        string binVersion = null;
                        XmlNodeList nlSol = _manifest.DocumentElement.SelectNodes("//solution/project");
                        foreach(XmlElement pe in nlSol)
                        {
                            if (pe.GetAttribute("file") == p.File)
                            {
                                binFile = pe.GetAttribute("binary");
                                SnapshotBinaryItemInfo asmInfo = asmInfoList.FindByBinFile(binFile);
                                if (asmInfo != null)
                                    binVersion = new AssemblyName(asmInfo.AssemblyName).Version.ToString();
                            }
                        }


                        if (!string.IsNullOrEmpty(binFile) && !string.IsNullOrEmpty(binVersion))
                        {
                            Debug.WriteLine("Build sattelite [" + culture + "] of '" + binFile + "'");

                            SdkUtils.BuildSattelite(
                                Path.Combine(WorkingFolder, binFile),
                                p.RootNamespace, 
                                culture, 
                                Path.GetDirectoryName(p.FullName),
                                binVersion, 
                                localFiles,
                                AssembliesInfo.GetFiles(WorkingFolder));
                        }
                    }
                }
            }
        }
        
        public void Extract()
        {
            using (FileStream ss = new FileStream(_fullName, FileMode.Open, FileAccess.Read, FileShare.Read, 32768))
            {
                const string manifestName = "Localization Preview Snapshot Manifest";

                {
                    using (ZipInputStream zs = new ZipInputStream(ss))
                    {
                        byte[] buffer = new byte[16384];
                        ZipEntry theEntry;
                        while ((theEntry = zs.GetNextEntry()) != null)
                        {
                            bool isManifest = theEntry.Name == manifestName;
                            if (!isManifest && theEntry.IsFile)
                            {
                                string outName = Path.Combine(WorkingFolder, theEntry.Name);
                                try { Directory.CreateDirectory(Path.GetDirectoryName(outName)); }
                                catch {}

                                using (FileStream os = new FileStream(outName, FileMode.Create, FileAccess.Write, FileShare.Read))
                                {
                                    for (int num = zs.Read(buffer, 0, buffer.Length);
                                         num > 0;
                                         num = zs.Read(buffer, 0, buffer.Length))
                                        os.Write(buffer, 0, num);
                                }
                            }
                        }
                    }
                }
            }
            
        }

        Dictionary<string, bool> _cacheHasPreview = new Dictionary<string, bool>();

        public bool FindType
            (
            string tmxContext,
            out SnapshotBinaryItemInfo assemblyInfo,
            out SnapshotType typeInfo
            )
        {
            assemblyInfo = null;
            typeInfo = null;

            SnapshotBinaryItemInfoCollection asmInfoList = this.AssembliesInfo;

            string[] arr = tmxContext.Split(':');
            if (arr.Length == 3)
            {
                string projectFile = arr[1];
                XmlNodeList nl = _manifest.SelectNodes("//solution/project");
                string projectBin = null;
                string rootNamespace = null;
                foreach (XmlElement pe in nl)
                {
                    if (pe.GetAttribute("file") == projectFile)
                    {
                        projectBin = pe.GetAttribute("binary");
                        rootNamespace = pe.GetAttribute("namespace");
                        break;
                    }
                }

                if (!string.IsNullOrEmpty(projectBin) && !string.IsNullOrEmpty(rootNamespace))
                {
                    string resKey = arr[2];
                    string expectedTypeName = rootNamespace + "." + resKey.Replace("\\", ".").Replace(" ", "_");
                    foreach (SnapshotBinaryItemInfo asmInfo in asmInfoList)
                    {
                        if (asmInfo.FileName == projectBin)
                        {
                            SnapshotType ti = asmInfo.Types.Find(expectedTypeName);
                            if (ti != null)
                            {
                                assemblyInfo = asmInfo;
                                typeInfo = ti;
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        public bool HasPreview(string tmxContext)
        {
            bool ret;
            if (_cacheHasPreview.TryGetValue(tmxContext, out ret))
                return ret;

            SnapshotBinaryItemInfo assemblyInfo;
            SnapshotType typeInfo;
            if (FindType(tmxContext, out assemblyInfo, out typeInfo))
            {
                _cacheHasPreview[tmxContext] = true;
                return true;
            }
            else
            {
                _cacheHasPreview[tmxContext] = false;
                return false;
            }
        }

        public static SnapshotInfo Create(string fullName)
        {
            XmlDocument doc = null;
            ZipEntry me = null;
            MemoryStream manifestCopy = null;
            if (File.Exists(fullName))
            {
                using (FileStream ss = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.Read, 32768))
                {
                    const string manifestName = "Localization Preview Snapshot Manifest";

                    using (ZipFile zFile = new ZipFile(fullName))
                        me = zFile.GetEntry(manifestName);

                    if (me != null)
                    {
                        using (ZipInputStream zs = new ZipInputStream(ss))
                        {
                            ZipEntry theEntry;
                            while ((theEntry = zs.GetNextEntry()) != null)
                            {
                                bool isManifest = theEntry.Name == manifestName;
                                if (isManifest)
                                {
                                    manifestCopy = new MemoryStream();
                                    byte[] buffer = new byte[16384];
                                    for (int num = zs.Read(buffer, 0, buffer.Length); num > 0; num = zs.Read(buffer, 0, buffer.Length))
                                        manifestCopy.Write(buffer, 0, num);

                                    manifestCopy.Seek(0, SeekOrigin.Begin);
                                    doc = new XmlDocument();
                                    doc.Load(manifestCopy);

                                    goto done;
                                }
                                else
                                    Debug.WriteLine("Skip zip entry " + theEntry.Name);
                            }
                        }
                    }
                }
            }

            done:
                return new SnapshotInfo(fullName, doc);
            
        }
    }
}
